//TODO: Make it easy for the user to know what they can say on any screen. When they say something that isn't recognized, pop up the commands they can say.

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Text;
using System.Windows.Forms;

namespace StarTrek
{
    /// <summary>
    /// The main screensaver form handles painting of the screen and also initialies
    /// the speech object.
    /// </summary>
    public partial class ScreenSaverForm : Form
    {
        #region Variables
        public static bool DEBUG = Properties.Settings.Default.Debug;

        // Keep track of whether the screensaver has become active.
        private bool isActive = false;

        // Keep track of the location of the mouse so we know when to kill the screensaver
        private Point mouseLocation;
        Point pntMouse = new Point(0, 0);

        private Speech sapi;

        // Fonts and brushes
        private Font fontHeader = new Font("Context Ultra Condensed SSi", 50, FontStyle.Regular);
        private Font fontBody = new Font("Context Ultra Condensed SSi", 30, FontStyle.Regular);
        Brush brushDarkYellow = new SolidBrush(Color.FromArgb(255, 153, 0));
        Brush brushLightPurple = new SolidBrush(Color.FromArgb(204, 153, 204));
        Brush brushLightBlue = new SolidBrush(Color.FromArgb(153, 153, 204));
        Brush brushMaroon = new SolidBrush(Color.FromArgb(204, 102, 102));
        Brush brushBeige = new SolidBrush(Color.FromArgb(255, 204, 153));
        Brush brushDarkBlue = new SolidBrush(Color.FromArgb(153, 153, 255));
        Brush brushOrange = new SolidBrush(Color.FromArgb(255, 153, 102));
        Brush brushDarkPurple = new SolidBrush(Color.FromArgb(204, 102, 153));
        #endregion

        #region Initialization
        public ScreenSaverForm()
        {
            //
            // Required for Windows Form Designer support
            //
            InitializeComponent();

            sapi = new Speech();
            sapi.ContentChangedEvent += new ContentChanged(sapi_ContentChangedEvent);

            SetupScreenSaver();
            Display.LoadBackgroundImage();

            backgroundChangeTimer.Interval = Int32.Parse(Properties.Settings.Default.Delay) * 60 * 1000;
        }

        /// <summary>
        /// When the speech class changes text, the ContentChanged event is raised.
        /// Redraw the screen.
        /// </summary>
        /// <param name="sender"></param>
        void sapi_ContentChangedEvent(object sender)
        {
            this.Refresh();
        }

        /// <summary>
        /// Set up the main form as a full screen screensaver.
        /// </summary>
        private void SetupScreenSaver()
        {
            // Use double buffering to improve drawing performance
            this.SetStyle(ControlStyles.OptimizedDoubleBuffer | ControlStyles.UserPaint | ControlStyles.AllPaintingInWmPaint, true);
            // Capture the mouse
            this.Capture = true;

            // Set the application to full screen mode and hide the mouse
            Cursor.Hide();
            Bounds = Screen.PrimaryScreen.Bounds;
            WindowState = FormWindowState.Maximized;
            ShowInTaskbar = false;
            DoubleBuffered = true;
            BackgroundImageLayout = ImageLayout.Stretch;
        }
        #endregion

        #region Form Events
        /// <summary>
        /// Form Load events. Initialize the speech engine.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void frmMain_Load(object sender, System.EventArgs e)
        {
            if (Properties.Settings.Default.EnableSpeech)
            {
                //initialize SAPI objects
                try
                {
                    sapi.initSAPI();

                    //loading initial grammar
                    sapi.SAPIGrammarFromFile("StarTrekCommands.xml");
                }
                catch (Exception ex)
                {
                    MessageBox.Show("Exception \n" + ex.ToString(), "Error - initSAPI");
                }
            }

            //hide the program from the taskbar
            this.ShowInTaskbar = false;
        }

        /// <summary>
        /// If the mouse has moved, end the application.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ScreenSaverForm_MouseMove(object sender, MouseEventArgs e)
        {
            // Set IsActive and MouseLocation only the first time this event is called.
            if (!isActive)
            {
                mouseLocation = MousePosition;
                isActive = true;
            }
            else
            {
                // If the mouse has moved significantly since first call, close.
                if ((Math.Abs(MousePosition.X - mouseLocation.X) > 10) ||
                    (Math.Abs(MousePosition.Y - mouseLocation.Y) > 10))
                {
                    Close();
                }
            }
        }

        /// <summary>
        /// Close the application if a key is pressed.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ScreenSaverForm_KeyDown(object sender, KeyEventArgs e)
        {
            Close();
        }

        /// <summary>
        /// Close the application if the mouse is clicked.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void ScreenSaverForm_MouseDown(object sender, MouseEventArgs e)
        {
            Close();
        }

        /// <summary>
        /// Cycle to the next background image
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void backgroundChangeTimerTick(object sender, EventArgs e)
        {
            Display.NextBackgroundImage();
            this.Refresh();
        }

        /// <summary>
        /// Paint the background. Determine what should be painted by looking at the
        /// current state of the Display object.
        /// </summary>
        /// <param name="e"></param>
        protected override void OnPaintBackground(PaintEventArgs e)
        {
            // Draw the current background image stretched to fill the full screen
            Image img = Display.MessageBackground;
            if (img == null)
            {
                img = Image.FromFile(Display.backgroundImageFileNames[Display.currentImageIndex]);
            }
            
            // Scale the background image
            int width = 0;
            int height = 0;
            if (img.Width / (double)Size.Width > img.Height / (double)Size.Height)
            {
                width = Size.Width;
                height = (int)((double)Size.Width * img.Height / img.Width);
            }
            else
            {
                width = (int)((double)Size.Height * img.Width / img.Height);
                height = Size.Height;
            }
            e.Graphics.DrawImage(img, 0, 0, width, height);

            // Display.MessageBackground is set when we are display information (not in picture mode)
            // TODO: Switch this to be based on an enum called DisplayState
            if (Display.MessageBackground != null)
            {
                WriteTitle(e.Graphics, Display.MessageTitle);
                WriteBody(e.Graphics, Display.MessageBody);
                WriteImage(e.Graphics, Display.MessageImage);
            }

            // If we are in debug mode, display information about the accuracy of the phrase we heard
            if (DEBUG)
            {
                e.Graphics.DrawString(Display.debugString, this.fontBody, this.brushOrange, new PointF(200, 10));
                // Set a timer to hide this information after a few seconds
                if (debugTimer.Enabled == false && Display.debugString != "")
                {
                    debugTimer.Enabled = true;
                }
            }
        }

        /// <summary>
        /// Hide the debug output after a few seconds.
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void debugTimer_Tick(object sender, EventArgs e)
        {
            Display.debugString = "";
            this.Refresh();
        }
        #endregion

        #region Display Helpers
        /// <summary>
        /// Write the title to the top-right of the screen
        /// </summary>
        /// <param name="g"></param>
        /// <param name="text"></param>
        private void WriteTitle(Graphics g, string text)
        {
            SizeF size = g.MeasureString(text, fontHeader);
            int x = (int)(this.Width - size.Width - 25);
            int y = 20;
            g.DrawString(text, fontHeader, brushDarkYellow, x, y);
        }

        /// <summary>
        /// Write the body of the message
        /// </summary>
        /// <param name="g"></param>
        /// <param name="text"></param>
        private void WriteBody(Graphics g, List<string> text)
        {
            // TODO: This is hardcoded for a 1280x1024 screen.
            // TODO: Scroll the message body if it doesn't fit on one screen
            int x = 200;
            int y = 370;
            int w = 1100;
            int h = 600;
            RectangleF rect = new RectangleF(x, y, w, h);
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < text.Count; i++)
            {
                sb.Append(Display.MessageBody[i] + "\n");
                //http://msdn2.microsoft.com/en-us/library/ms533909.aspx                    
            }
            g.DrawString(sb.ToString(), fontBody, brushLightPurple, rect);
        }

        /// <summary>
        /// If there is an image to display, show it in the lower right of the screen
        /// </summary>
        /// <param name="g"></param>
        /// <param name="img"></param>
        private void WriteImage(Graphics g, Image img)
        {
            if (img == null)
            {
                return;
            }

            int margin = 50;
            int border = 10;
            int x = Size.Width - img.Width - margin;
            int y = Size.Height - img.Height - margin;
            g.FillRectangle(brushMaroon, new Rectangle(x, y, img.Width + 2 * border, img.Height + 2 * border));
            g.DrawImage(Display.MessageImage, x + border, y + border, img.Width, img.Height);
        }
        #endregion


    }
}